package com.swa99ylum.pxf.row;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRow;

import com.swa99ylum.pxf.Filler;
import com.swa99ylum.pxf.bean.Index;
import com.swa99ylum.pxf.cell.CellFiller;
import com.swa99ylum.pxf.cell.indexer.CellIndexer;
import com.swa99ylum.pxf.cell.indexer.FixedCellIndexer;
import com.swa99ylum.pxf.row.indexer.FixedRowIndexer;
import com.swa99ylum.pxf.row.indexer.RowIndexer;


//TODO: RowFiller 是不是 需要有 公式、样式、单元格类型
//TODO: "row 的 样式" 应当在 "cell 的 样式" 之前设置
public class RowFiller extends Filler {
	
	private static Logger logger = Logger.getLogger(RowFiller.class);
	
	private int beginIndex;
	//private int endIndex; //不需要 endIndex
	private RowIndexer rowIndexer;
	
	
	/**
	 * row 和 cellFillers 之间是 "多对一" 的 关系，
	 * 即 "多行row" 可以使用 "一组cellFiller" 进行填充
	 */
	private XSSFRow[]		rows;  //SheetFiller.fill() 的时候会 赋值
	
	
	private CellFiller[]	cellFillers;
	
	private List<?> dataList;
	
	/**
	 * 构建 "合并单元格" 时，所要用到的 "黏连" 配置
	 */
	private boolean adherePreviousRow;	//"本组 RowFiller 的 第一行"是否允许和 "上一行" 发生 黏连  
	
	
	/** 
	 * 默认："本组 RowFiller 的 第一行" 不黏连 "上一行"
	 * 隔断 "本组 RowFiller" 和 上方的 黏连
	 */
	private final static boolean	DEFAULT_ADHERE_PREVIOUS_ROW = false;


	/**
	 * styleMap 的 注入链
	 */
	@Override
	public void spreadStyleMap(Map<String, XSSFCellStyle> styleMap) {
		for (CellFiller cellFiller : cellFillers) {
			cellFiller.spreadStyleMap(styleMap);
		}
	}
	
	
	/*--------------------------------------------
    |      			7 个  构造器			         |
    ============================================*/
	public RowFiller() {
		super();
	}
	
	/* 2  个 带有 Indexer 的 构造器	 */
	public <T> RowFiller(RowIndexer rowIndexer, List<T> dataList, CellFiller[] cellFillers, boolean adherePreviousRow) {
		super();
		
		this.beginIndex = -1;
		
		this.rowIndexer = rowIndexer;
		this.cellFillers = cellFillers;
		this.dataList = dataList;
		
		this.adherePreviousRow = adherePreviousRow;
	}
	public <T> RowFiller(RowIndexer beginIndex, List<T> dataList, CellFiller[] cellFillers) {
		
		this(beginIndex, dataList, cellFillers, DEFAULT_ADHERE_PREVIOUS_ROW);
	}
	
	
	/* 2  个 带有 beginIndex 的 构造器	 */
	public <T> RowFiller(int beginIndex, List<T> dataList, CellFiller[] cellFillers, boolean adherePreviousRow) {
		
		this(new FixedRowIndexer(beginIndex), dataList, cellFillers, adherePreviousRow);
	}
	public <T> RowFiller(int beginIndex, List<T> dataList, CellFiller[] cellFillers) {
		
		this(beginIndex, dataList, cellFillers, DEFAULT_ADHERE_PREVIOUS_ROW);
	}
	
	/* 2  个 不带 index 信息 的 构造器	 */
	public <T> RowFiller(List<T> dataList, CellFiller[] cellFillers, boolean adherePreviousRow) {
		/* 
		 * 如果 rowIndexer 为 null，那么 在 SheetFiller.fill() 时，
		 * 会为 rowIndexer 主动创建一个 FixedRangeRowIndexer
		 */
		this(null, dataList, cellFillers, adherePreviousRow);
	}
	public <T> RowFiller(List<T> dataList, CellFiller[] cellFillers) {
		
		this(null, dataList, cellFillers, DEFAULT_ADHERE_PREVIOUS_ROW);
	}
	

	/**
	 * 核心方法
	 */
	public void fill() throws Exception {
//		@SuppressWarnings("unchecked")
//		List<?> eleList = hookList;
//		T ele = clazz.newInstance();
//		eleList.add(ele);
//		Object ele;
		
		//TODO: 这里 应该 循环的是 dataList，但其实 循环 rows 也是一样的，因为 sheetFiller 中
		//TODO: 会根据 dataList 的 size，来 创建 rows
		for (int rowIndex = 0; rowIndex<rows.length; rowIndex++) {
			XSSFRow row = rows[rowIndex];
			Object data = dataList.get(rowIndex);
			
			
			boolean allowVetAdhesive; //是否 允许使用 "纵向粘合能力"
			
			
			

			/**
			 * 因为 开启了 IfRenew=true，所以 "本组 RowFiller 的 第一行"  不会试图 黏连 上一行
			 * 
			 * 每次 RowFiller 开始，都会跟胡 IfNew ，来给 flag 赋值
			 * TODO：应当写在 这个 for 循环外面
			 */
		
			
//			printRow(row);
			
			
//			for (CellParser cellParser : cellParsers) {
//				//1、构建 XSSFCell[] cells
//				int beginIndex = cellParser.getBeginIndex();
//				int endIndex = cellParser.getEndIndex();
//				
//				XSSFCell[] cells = new XSSFCell[endIndex-beginIndex+1];
//				
//				for(int i=beginIndex; i<endIndex+1; i++) {
//					int index = i-beginIndex;
//					cells[index] = row.getCell(i); //row.getCell(i) 的结果 可能为 null
//					
//					if(!cellIndexSet.add(i)) {
//						/*
//						 * 1) 同一 row   的 多个 cell，不应当出现 cellIndex 之间的 重叠
//						 * 	对于一个 row 来说，有 2 个 CellParser 中 都同时包含了 (1行,3列) 这个单元格 的 cellValue，这其实是没有任何意义的
//						 * 
//						 * 2) 同一 sheet 的 多个  row，	可以出现  rowIndex 之间的 重叠
//						 * 	1. 第一次 可以 解析 1-6 行（row）的 0-10 列；第二次 可以解析 1-6 行（row）的 11-20 列
//						 * 	2. 可以对 1-6 行（row）的 0-10 列 进行反复的 解析
//						 * 
//						 */
//						String msg = row.getSheet().getSheetName() + "页，"
//									+ row.getRowNum() + "行"
//									+ i + "列 的 单元格 被 重复提取了";
//						
//						throw new IndexOverlappingException(msg);
//					}
//				}
//				
//				//2、将 cells 交给  cellParser
//				cellParser.setCells(cells);
//				
//				//3、解析 cellParser 中的  XSSFCell[] cells（提取值）
//				cellParser.parse();
//				
//			}
			
			
			/**
			 * 缺省的 默认值为 -1，因为下面有 cellIndexer = new FixedRangeCellIndexer(cellIndex+1); 会把 -1+1=0 
			 * 否则当 第一个 CF 没有设置 index 信息时，第一列 会 空开一列
			 */
			int globalCellIndex = -1;
			XSSFCell cell;
			for (CellFiller cellFiller : cellFillers) {
				
				//20180714
				
				if( rowIndex==0 /** && !adherePreviousRow */) {
					
					if(cellFiller.isAllowVetAdhesive() == null) {
						allowVetAdhesive = adherePreviousRow;
					}else {
						allowVetAdhesive = cellFiller.isAllowVetAdhesive();
					}
					/* 
					 * 如果：
					 * 1、当前行 为 本组第一行
					 * 2、"本组 RowFiller 的 第一行" 不黏连 "上一行"
					 * 
					 * 那么：
					 * 不做任何 纵向黏连 操作，即：则 该行上的  所有 cell 都不会和 上一行 发生黏连
					 */
					
//					allowVetAdhesive = false;
				}else {
					allowVetAdhesive = true;
				}
				
				
				List<XSSFCell> cellList = new ArrayList<>();	//待填值
				List<Object> cellValueList = new ArrayList<>();	//待填值
				
				Object[] properties = cellFiller.getProperties(); //规定了 宽度，是下面 循环的 标准
				CellIndexer cellIndexer = cellFiller.getCellIndexer();
				
				
				//TODO：if cellIndexer == null，如果没有创建 cellIndexer，则 主动创建 一个
				if(cellIndexer == null) {
					cellIndexer = new FixedCellIndexer(globalCellIndex+1);
					cellFiller.setCellIndexer(cellIndexer);
					
				}else if(cellIndexer.hasParent()) {
					
					cellIndexer.cloneParentIndex();
					
				}else if(!cellIndexer.hasParent()) {
					
					if(cellIndexer.getIndex() == null) {
						cellIndexer.setIndex(new Index(globalCellIndex+1));
						
					}else if(cellIndexer.getIndex() != null) {
						/**
						 * CF 的 indexer 使用了 "使用 new Index() 创建" 的 index，
						 * 使用 此方式 创建的 index，值为 -1
						 */
						Index index = cellIndexer.getIndex();
						
						if(index.getValue() == -1) {
							index.initValue(globalCellIndex+1);
						}
					}
				}
				
				
				
				while(true) {
					cell = row.getCell(cellIndexer.getIndexValue());
					
					if(cellIndexer.triggerBeginning(cell)) 	break;
					else									cellIndexer.incrementIndexValue();
				}
				
				
				//TODO:1、map.getKeys() 的 返回值 可以交给 CF，作为 properties[] 中的值
				//TODO:2、pxf 应当也支持 List<String>、List<Map<String, Object>>
				//TODO:3、当 CF 为 List<String> 是，properties[] 要放什么 东西？
				//TODO:4、帮 雷倩 做 demo，1、建表 2、整理成 Map<String, <Map<String, Object>> ，在通过 map.getValues 转成 List<Map<String, Object>>
				
				
				for (Object property : properties) {
					
					Object cellValue = getCellValue(data, property);
					
					cellValueList.add(cellValue);
					
//					logger.debug("======!!!!!!!!!!!================> " + i);
					
					globalCellIndex = cellIndexer.getIndexValue();
					
					cell = row.getCell(globalCellIndex);
					/* if(cell==null) break; */  //拿到 cell 之后，不应当做 任何 操作、判断，要 原封原样 交给 indexer
					
					/** 如果有 "null 单元格"，则将他 补为 "blank 单元格" */
					if(cell == null) {
						cell = row.createCell(globalCellIndex);
						
						//System.out.println(cell.getCellType()); //CellType 为 blank
					}
					
					cellList.add(cell);
					
					cellIndexer.incrementIndexValue();
					
				}
				
				XSSFCell[] cells = (XSSFCell[]) cellList.toArray(new XSSFCell[0]);
				Object[] cellValues = (Object[]) cellValueList.toArray(new Object[0]);
				
				logger.debug("收集的 列数：" + cells.length);
				
				//2、将 cells 交给  cellParser
				cellFiller.setCells(cells);
				cellFiller.setCellValues(cellValues);
				
				cellFiller.setAllowVetAdhesive(allowVetAdhesive);
				
				//3、解析 cellParser 中的  XSSFCell[] cells（提取值）
				cellFiller.fill();
				
				
			}
			
			
			//TODO: 多个行 享用一组 CellParser，但是 CellIndexer 是不能 跨行的，要把 CellIndexer 的 恢复初始情况，即 reset
			//解决：方法1：深拷贝？  方法2：在 Indexer 中设计一个 reset 方法 
			
			//reset 之后，才能为 新的 行服务
			//注意：有时 cellParsers 的 indexer 会共享，也就是说 
			//多次 cellParser.getCellIndexer().resetIndex(); 可能 恢复的 是 同一个对象
			//不然会发生 无限循环
			
			if(rowIndex != (rows.length-1)) {//当前 row 不为最后一个 row
				
				for (CellFiller cellFiller : cellFillers) {
					resetIndex(cellFiller);
				}
				
			} else {//当前 "行" 为 "最后一行"
				
				checkUnreset(); //检查是否需要进行 unreset 操作
			}
			
		}
	}
	
	
	
	private void checkUnreset() {
		Set<Index> set = new HashSet<Index>();
		
		//step1、收集 unreset 的 index 对象
		for (CellFiller cellFiller : cellFillers) {
			CellIndexer cellIndexer = cellFiller.getCellIndexer();
			Index index = cellIndexer.getIndex();
			
			if(cellIndexer.isUnreset() == true) set.add(index);
		}
		
		//step2、
		/**
		 * 如果 当前 CF 的 index 属于 unreset 的
		 * 1) 如果 CF.isUnreset()==true，则 将 value0 更新为 当前的 columnIndex
		 * 2) 如果 CF.isUnreset()==false，则不做 任何操作
		 * 
		 * 如果 当前 CF 的 index 不属于 unreset 的，则对 该 index 进行 reset 操作
		 * 
		 */
		for (CellFiller cellFiller : cellFillers) {
			CellIndexer cellIndexer = cellFiller.getCellIndexer();
			Index index = cellIndexer.getIndex();
			
			if(set.contains(index)) {
				
				if(cellIndexer.isUnreset() == true) {
					//不进行 index 归 0；并且 将 value0 更新为 当前的 columnIndex
					cellIndexer.getIndex().initValue( cellIndexer.getIndexValue() );
					
				}else {
					//什么都不做
				}
				
			}else {
				
				resetIndex(cellFiller);
			}
		}
	}
	
	/**
	 * 将 CF 的 index 进行 reset，从而为 下一个 row 服务
	 */
	private void resetIndex(CellFiller cellFiller) {
		CellIndexer cellIndexer = cellFiller.getCellIndexer();
		
		cellIndexer.resetIndexValue();
		cellFiller.setBeginIndex(-1);
	}
	
	

	/**
	 * 对于 pxf 来说，@{...} 是个特殊字符
	 * @{name}：去 Student 中取 name 属性的 值，作为 单元格的值
	 * 
	 * 1、那如果真的要 在 单元格中 填入 "@{name}" 这个字符串 怎么办？( 参考"转义字符"的做法 )
	 * @{@{name}}:			表示 纯文本 "@{name}"，因为  "@{name}" 不能作为 变量的名称，参见下面的 2、
	 * @{@{@{name}}}:		表示 纯文本 "@{@{name}}"
	 * @{@{@{@{name}}}}:	表示 纯文本 "@{@{@{name}}}"
	 * 以此类推...
	 * 
	 * 2、另一个重要的 原因是：@{name} 不能作为 变量名
	 * String @{name} = "jack"; //编译出错：错误的 变量名 
	 * 
	 * 3、这种做法和在 MyBatis 的 sql 中"使用 #{name} 作为 占位符"是同一个 道理
	 * sql  中 => #{...} 是错误的 语法
	 * java 中 => @{...} 是错误的 变量名，所以 我使用 @{...} 作为 "属性值标识符"
	 */
	public/*private*/ Object getCellValue(Object data, Object property) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		Object cellValue;
		
		
		if(property instanceof String) {
			Pattern pattern1 = Pattern.compile("@\\{([\\s\\S]*?)\\}"); //匹配 @{...}
			Matcher matcher1 = pattern1.matcher((String) property);
			
			if (matcher1.matches()) {//javaBean 的 属性
				
				Matcher matcher2 = pattern1.matcher(matcher1.group(1));
				
				if (matcher2.matches()) { //@{@{name}}、@{@{@{name}}}
					 /**
					  * 有多层 @{...}，例如：@{@{name}}
					  * 处理方式：转义一次，将 纯文本 "@{@{name}}" 转为 纯文本"@{name}"
					  */
					cellValue = matcher1.group(1);
					
				}else { //@{name}
					 /**
					  * 只有一层 @{...}，例如：@{name}
					  * 处理方式：取 student 对象的 name 属性，作为返回值
					  */
					
//					System.out.println("==> 匹配成功");//@{name}, @{age}
//					
//					System.out.println(0 + "::\"" + matcher1.group(0) + "\"");
//					System.out.println(1 + "::\"" + matcher1.group(1) + "\"");
//					System.out.println(2 + "::\"" + matcher1.group(2) + "\"");
					
					String propertyName = matcher1.group(1);
					
					
					if(data instanceof String || data.getClass().isPrimitive()) {
						//String 或 基本类型：则 直接返回 变量自身
						
						return data;
						
					}else if(data instanceof Map) {
						
						/** 下面 5 种方式 居然都可以 */
						//1
//							@SuppressWarnings("unchecked")
//							Map<String, Object> map =  (Map<String, Object>) data;
						
						
						//2
//							@SuppressWarnings("unchecked")
//							Map<String, String> map =  (Map<String, String>) data;
						
						//3
//							@SuppressWarnings("unchecked")
//							Map<Integer, Integer> map =  (Map<Integer, Integer>) data;
						
						
						//4
//							@SuppressWarnings("unchecked")
//							Map<Integer, Object> map =  (Map<Integer, Object>) data;
						
						
						//5
						Map<?, ?> map =   (Map<?, ?>) data;
						
						cellValue = map.get(propertyName);
						
						
						//TODO: 当 使用 map 时，key 找不到，则 返回 null，不会报错
						//TODO: 当 使用 javaBean 时，PropertyUtils.getProperty() 找不到 对应的 属性，则 程序会 抛异常: NoSuchMethodException
						
					}else {
						
						try {
							cellValue = PropertyUtils.getProperty(data, propertyName);
						} catch (NoSuchMethodException e) {
							cellValue = null; 
							/**
							 * 当 Student 没有 aaa 属性时，返回 null，不报错；和 map 的 做法一致
							 * 原因是：可以让 injector 去 考虑：如何去处理 这个 null 值
							 *  
							 */
							
						}
						/**
						 * 当 Student 没有 aaa 属性时，PropertyUtils.getProperty() 会报错：
						 * Exception in thread "main" java.lang.NoSuchMethodException: Unknown property 'aaa' on class 'Student'
						 * 
						 * 因为 这个 操作 具有 "开发者的 主观性"，所以不应当 try-catch 这个错误
						 */
					}
					
				}
				
				
			}else {//纯文本
//				System.out.println("==> 匹配失败");
				
				cellValue = property;
			}
			
		}else {//纯文本
			cellValue = property;
		}
		
		return cellValue;
		
	}
	
	
	
	/*--------------------------------------------
    |      			getter、setter		         |
    ============================================*/
	
	public int getBeginIndex() {
		return beginIndex;
	}

	public void setBeginIndex(int beginIndex) {
		this.beginIndex = beginIndex;
	}

	public RowIndexer getRowIndexer() {
		return rowIndexer;
	}

	public void setRowIndexer(RowIndexer rowIndexer) {
		this.rowIndexer = rowIndexer;
	}

	public XSSFRow[] getRows() {
		return rows;
	}

	public void setRows(XSSFRow[] rows) {
		this.rows = rows;
	}

	public CellFiller[] getCellFillers() {
		return cellFillers;
	}

	public void setCellFillers(CellFiller[] cellFillers) {
		this.cellFillers = cellFillers;
	}

	public List<?> getDataList() {
		return dataList;
	}

	public void setDataList(List<?> dataList) {
		this.dataList = dataList;
	}
	
}
